package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.hmall.common.utils.CollUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * 实时更新路由配置信息
 */

@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicRouteLoader {

    private final NacosConfigManager nacosConfigManager; //用于创建与nacos的连接服务
    private final RouteDefinitionWriter routeDefinitionWriter;//更新路由的工具
    private final String dataId="gateway-routes.json";
    private final String group= "DEFAULT_GROUP";

    private final Set<String> routeIds=new HashSet<>();//保存所有路由id
    @PostConstruct //在DynamicRouteLoader bean初始化后执行，调用该方法
    public void initRouteConfigListener() throws NacosException {
        //1.项目启动，先拉取一次配置，并且添加配置监听器
        String configInfo = nacosConfigManager.getConfigService() //返回一个可以操作 Nacos 配置中心的服务实例
                //拉取路由配置并且监听路由配置是否发生改变
                .getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        //2.监听到配置变更，需要去更新路由表
                        updateConfigInfo(configInfo);
                    }
                });
        //3.第一次读取的配置也需要更新到路由表
        updateConfigInfo(configInfo);
    }

    /**
     * 更新路由表信息
     * @param configInfo
     */
    public void updateConfigInfo(String configInfo){
        log.info("监听到路由配置信息:{}",configInfo);
        // 1.解析配置信息，转为RouteDefinition
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2.删除所有路由表(统一删减路由表的情况)
        for (String routeId : routeIds) {
            routeDefinitionWriter.delete(Mono.just(routeId)).subscribe();
        }
        routeIds.clear();//清空
        // 2.2.判断是否有新的路由要更新
        if (CollUtils.isEmpty(routeDefinitions)) {
            // 无新路由配置，直接结束
            return;
        }
        //3.更新路由表
        for (RouteDefinition routeDefinition : routeDefinitions) {
            routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
            routeIds.add(routeDefinition.getId());
        }
    }
}
